﻿#include<iostream>
using namespace std;
//函数重载：是函数的一种特殊情况，C++允许在同一作用域中声明几个功能类似的同名函数，这
//些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同，常用来处理实现功能类似数据类型
//不同的问题。

//不同作用域可以同名
//namespace wxmh1 {
//	void Swap(int* pa, int* pb) {
//		cout << "void Swap(int* pa, int* pb)" << endl;
//	}
//}
//namespace wxmh2 {
//	void Swap(int* pa, int* pb) {
//		cout << "void Swap(int* pa, int* pb)" << endl;
//	}
//}
////using namespace wxmh1;
////using namespace wxmh2;//两个命名域都展开使用时，如果不加权限会存在歧义，且不是重载关系
//int main() {
//	int a = 0, b = 5;
//	double c = 0.3, d = 2.22222;
//	wxmh1::Swap(&a, &b);
//	wxmh2::Swap(&a, &b);
//	//Swap(&a,&b);
//	//Swap(&a,&b);//调用歧义
//
//}

////同一作用作用域，满足重载规则，可以同名
// //重载规则：同一作用域同名函数，参数类型不同，参数个数不同，参数类型顺序不同
//void Swap(int* pa, int* pb) {
//	cout << "void Swap(int* pa, int* pb)" << endl;
//}
//void Swap(double* px, double* py) {
//	cout << "void Swap(int* px, int* py)" << endl;
//}
// void f(int a) {
//cout << "void f(int a)" << endl;
//}
// void f(){
// cout << "void f()" << endl;
// }
//int main() {
//	int a = 0, b = 5;
//	double c = 0.3, d = 2.22222;
//	Swap(&a,&b);
//	Swap(&c,&d);
// f();
// f(a);
//}

////下面两个函数返回值不同，函数名和参数相同，无法区分，返回值不是重载的条件
////void f() {
////	cout << "void f()" << endl;
////}
////int f() {
////	cout << "void f()" << endl;
////	return 0;
////}
////下面两个构造函数重载存在歧义
////f()调用时存在歧义
////void f() {
////	cout << "void f()" << endl;
////}
////void f(int a=10) {
////	cout << "void f(int a)" << endl;
////}
//int main() {
//	int a = 3;
//	//f();//
//	//f();
////int ret=f();
//	return 0;
//}

//为什么C语言不支持函数重载C++支持？
// C语言没办法支持重载，因为同名函数没办法区分。而C++是通过函数修
//饰规则来区分，只要参数不同，修饰出来的名字就不一样，就支持了重载。


//引用的概念
//引用不是新定义一个变量，而是给已存在变量取了一个别名，编译器不会为引用变量开辟内存空
//间，它和它引用的变量共用同一块内存空间。
//void Swap(int& pa, int& pb) {
//	//参数传过来相当于被取了别名，通过别名来完成内容交换
//	//不同于传地址，之前函数接收的是实参的形参，形参是实参的临时拷贝，形参的改变
//	//不会影响实参，所以函数用指针接收的是实参的地址，通过地址来改变实参的内容。
//	int tmp = pa;
//	pa = pb;
//	pb = tmp;
//}
//int main() {
//	//int a = 0;
//	////引用a是b的别名
//	//int& b = a;
//	//int& c = a;
//	////别名的别名
//	//int& d = b;
//	//d++;
//	//cout << &a << endl;
//	//cout << &b << endl;
//	//cout << &c << endl;
//	//cout << &d << endl;
//	int a = 5;
//	int b = 9;
//	 Swap(a,b);//
//	 cout << a << endl;
//	cout << b << endl;
//	return 0;
//}
typedef struct SeqList {
	int* a;
	//....
}STL;
//void SeqPushBack(struct SeqList& sl, int x);//给这个结构体起了个别名，通过别名改变了内容
void SeqPushBack(STL& s1,int x){}//给重定义的名字起了个别名

typedef struct ListNode {
	int val;
	struct ListNode* next;
}LTNode,*PNode;
//类似于
typedef struct ListNode* PNode;
//void ListPushBack(LTNode** pphead, int x);
//void ListPushBack(LTNode*& phead, int x);
// 
//void ListPushBack(PNode& phead, int x);

//int main() {
//	//给指针变量取别名
//	int a = 7;
//	int* p = &a;
//	int*& q = p;
//	q = NULL;
//	//int& a;//不能先定义别名，没有主体
//	int x = 0;
//	int& y = x;
//	int z = 1;
//	y = z;//z赋值给y
//
//	return 0;
//
//}
//
//int main() {
//	//权限平移
//	int x = 0;
//	int& y = x;
//	//权限缩大变小可以
//	const int& z = x;
//	//z++;const修饰z，z的值不能发生改变
//	x++;//因为z是x的别名，但是x的权限比z的权限大，所以x的值改变z的值就会改变。
//	y++;//y是x的别名，y的权限和x的权限一样，y的值改变，x的值就会改变，z也会改变。
//
//	//m的值只读不可以修改，它的别名权限不能比主体权限大。
//	const int m = 0;
//	//int& n = m;//直接标红
//	const int& n = m;//正确写法
//	//m拷贝给j，j的修改不受m影响。
//	int j = m;
//
//	const int* p1 = &m;//p1所指向的地址不能被修改,p1可以被修改
//	//p1++;√
//	//int*& p2 = p1;//错误写法，权限别名比主体权限大。
//
//	//权限的缩小
//	int* p3 = &x;
//	const int*& p4 = p3;
//	return 0;
//}

//int main() {
//	//权限可以平移/缩小 不能放大
//	double d = 12.34;
//	//类型转换，会存在一个临时变量，浮点数存储使整数和小数部分使分开存储的
//	int i = d;
//	//临时变量具有常性
//	//int& r = d;//×
//	const int& r = d;//√
//	return 0;
//}
//
//int main() {
//	int a = 0;
//	//语法上不开空间
//	int& b = a;
//	//语法上要开空间
//	int* p = &a;
//	//底层都是一样的，指针存的
//
//	//int* ptr = NULL;
//	//int& r = *ptr;//这里没有解引用，不会报错
//	//cout << r << endl;//这里开始解引用，会报错
//	cout << sizeof(b) << endl;
//	cout << sizeof(int&) << endl;
//	cout << sizeof(char&) << endl;
//	cout << sizeof(int*) << endl;
//	cout << sizeof(char*) << endl;
//	//在sizeof中含义不同：引用结果为引用类型的大小，但指针始终是地址空间所占字节个数(32
//		//位平台下占4个字节)
//
//	//引用自加即引用的实体增加1，指针自加即指针向后偏移一个类型的大小
//	b++;
//	p++;
//	//7. 有多级指针，但是没有多级引用
//	//8. 访问实体方式不同，指针需要显式解引用，引用编译器自己处理
//	//9. 引用比指针使用起来相对更安全
//	return 0;
//	//引用不能完全代替指针，在单链表中引用不能改变指向
//}

//内联函数 概念
//以inline修饰的函数叫做内联函数，编译时C++编译器会在调用内联函数的地方展开，没有函数调
//用建立栈帧的开销，内联函数提升程序运行的效率。（频繁调用的小函数）
//Debug版本下面默认不展开，为了方便调试
//inline int Add(int a, int b) {
//	int c = a + b;
//	cout << c << endl;
//	return c;
//}
//int main() {
//	int c = Add(1, 2);
//
//	return 0;
//}
// 特性
//1. inline是一种以空间换时间的做法，如果编译器将函数当成内联函数处理，在编译阶段，会
//用函数体替换函数调用，缺陷：可能会使目标文件变大，优势：少了调用开销，提高程序运
//行效率。
//2. inline对于编译器而言只是一个建议，不同编译器关于inline实现机制可能不同，一般建
//议：将函数规模较小(即函数不是很长，具体没有准确的说法，取决于编译器内部实现)、不
//是递归、且频繁调用的函数采用inline修饰，否则编译器会忽略inline特性。下图为
//《C++prime》第五版关于inline的建议：
//内联说明只是向编译器发出的一个请求，编译器可以选择忽略这个请求。
//一般来说，内联机制用于优化规模较小流程直接、频繁调用的函数。
//多编译器都不支持内联递归函数，而且一个75 行的函数也不大可能在调用点内联地展开

//inline不建议声明和定义分离，分离会导致链接错误。因为inline被展开，就没有函数地址
//了，链接就会找不到
//#include"Fun.h"
//int main() {
//	Fun(10);//call ?f@@YAXH@Z(?)
//	return 0;
//}
//宏的优缺点？
//优点：
//1.增强代码的复用性。
//2.提高性能。
//缺点：
//1.不方便调试宏。（因为预编译阶段进行了替换）
//2.导致代码可读性差，可维护性差，容易误用。
//3.没有类型安全的检查 。
//C++有哪些技术替代宏？
//1. 常量定义 换用const enum
//2. 短小函数定义 换用内联函数


//auto关键字(C++11)
// auto不再是一个存储类型指示符，而是作为一
//个新的类型指示符来指示编译器，auto声明的变量必须由编译器在编译时期推导而得。
//#include <string>
//#include <map>
//int main()
//{
//	//1. 类型难于拼写,std::map<std::string, std::string>::iterator 是一个类型，但是该类型太长了，特别容
//	//易写错。
//	std::map<std::string, std::string> m{ { "apple", "苹果" }, { "orange",
//   "橙子" },
//	  {"pear","梨"} };
//	std::map<std::string, std::string>::iterator it = m.begin();
//	while (it != m.end())
//	{
//		//....
//	}
//	return 0;
//}


//2. 含义不明确导致容易出错
//通过typedef给类型取别名
//typedef char* pstring;
//int main()
//{
//const pstring p1;   //相当于 const char* p1,p1需要被初始化
//	const pstring* p2;
//	return 0;
//}
//int TestAuto() {
//	return 10;
//}
//int main() {
//	/*int a = 10;
//	auto b = a;
//	auto c = 'a';
//	auto d = TestAuto();
//	cout << typeid(b).name() << endl;
//	cout << typeid(c).name() << endl;
//	cout << typeid(d).name() << endl;*/
//  //auto e; //无法通过编译，使用auto定义变量时必须对其进行初始化
//	int x = 10;
//	auto a = &x;//与下面写法等价，可以传任意类型
//	auto* b = &x;//但是传入的必须使指针类型
//	//引用
//	auto& c = x;
//	return 0;
//}
//使用auto定义变量时必须对其进行初始化，在编译阶段编译器需要根据初始化表达式来推导auto
//的实际类型。因此auto并非是一种“类型”的声明，而是一个类型声明时的“占位符”，编译器在编
//译期会将auto替换为变量实际的类型。


//auto不支持的写法
//int TestAuto1(){
//	auto a = 0, b = 2;
//	//auto c = 3, d = 2.22;//// 该行代码会编译失败，因为c和d的初始化表达式类型不同
//}

// 此处代码编译失败，auto不能作为形参类型，因为编译器无法对a的实际类型进行推导
//void TestAuto(auto a)
//{}

//void TestAuto()
//{
//	int a[] = { 1,2,3 };
//	auto b[] = { 4，5，6 };//auto不能直接用来声明数组
//}


//基于范围的for循环
//对于一个有范围的集合而言，由程序员来说明循环的范围是多余的，有时候还会容易犯错误。因
//此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ ：”分为两部分：第一部分是范
//围内用于迭代的变量，第二部分则表示被迭代的范围。

//C++11 范围for
//自动取数组array中，赋值给e
//自动++，自动判断结束
//不支持
//void TestFor(int array[]) {
//	for (auto& e : array) {
//		e /= 2;
//		cout << e << endl;
//	}
//}
//int main() {
//	int array[] = {2,4,6,8};
//	for (auto& e : array) {
//		e /=2;
//		cout << e << endl;
//	}
//	for (int x : array) {
//		cout << x << endl;
//	}
//}

//NULL实际是一个宏，在传统的C头文件(stddef.h)中，可以看到如下代码：
//#define NULL   0
//NULL可能被定义为字面常量0，或者被定义为无类型指针(void*)的常量。不论采取何
//种定义，在使用空值的指针时，都不可避免的会遇到一些麻烦
//程序本意是想通过f(NULL)调用指针版本的f(int*)函数，但是由于NULL被定义成0，因此与程序的
//初衷相悖。
//void f(int i) {
//	cout << "f(int)" << endl;
//}
//void f(int* p) {
//	cout << "f(int*)" << endl;
//}
//int main() {
//	f(0);
//	f(NULL);
//	f((int*)NULL);
//	f(nullptr);
//	return 0;
//}
//注意：
//1. 在使用nullptr表示指针空值时，不需要包含头文件，因为nullptr是C++11作为新关键字引入
//的。
//void TestPtr()
//{
//	int* p1 = NULL;
//	int* p2 = 0;
//	// ……
//}
//#ifndef NULL
//#ifdef __cplusplus
//#define NULL   0
//#else
//#define NULL   ((void *)0)
//#endif
//#endif
//void f(int)
//{
//	cout << "f(int)" << endl;
//}
//void f(int*)
//{
//	cout << "f(int*)" << endl;
//}
//int main()
//{
//	f(0);
//	f(NULL);
//	f((int*)NULL);
//	return 0;
//}
//注意：
//1. 在使用nullptr表示指针空值时，不需要包含头文件，因为nullptr是C++11作为新关键字引入
//的。
//2. 在C++11中，sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
//3. 为了提高代码的健壮性，在后续表示指针空值时建议最好使用nullptr。
